#import <objc/runtime.h>
#import "JastorRuntimeHelper.h"
#import "Jastor.h"

static const char *property_getTypeName(objc_property_t property) {
    const char *attributes = property_getAttributes(property);
    char buffer[1 + strlen(attributes)];
    strcpy(buffer, attributes);
    char *state = buffer, *attribute;
    while ((attribute = strsep(&state, ",")) != NULL) {
        if (attribute[0] == 'T') {
            size_t len = strlen(attribute);
            attribute[len - 1] = '\0';
            return (const char *)[[NSData dataWithBytes:(attribute + 3) length:len - 2] bytes];
        }
    }
    return "@";
}

@implementation JastorRuntimeHelper

static NSMutableDictionary *propertyListByClass;
static NSMutableDictionary *propertyClassByClassAndPropertyName;
+ (BOOL)isPropertyReadOnly:(Class)klass propertyName:(NSString*)propertyName{
    const char * type = property_getAttributes(class_getProperty(klass, [propertyName UTF8String]));
    NSString * typeString = [NSString stringWithUTF8String:type];
    NSArray * attributes = [typeString componentsSeparatedByString:@","];
    NSString * typeAttribute = [attributes objectAtIndex:1];
    
    return [typeAttribute rangeOfString:@"R"].length > 0;
}

+ (NSArray *)propertyNames:(Class)klass {
    if (klass == [Jastor class]) {
        return [NSArray array];
    }
    if (!propertyListByClass) {
        propertyListByClass = [[NSMutableDictionary alloc] init];
    }
    
    NSString *className = NSStringFromClass(klass);
    NSArray *value = [propertyListByClass objectForKey:className];
    
    if (value) {
        return value;
    }
    
    NSMutableArray *propertyNamesArray = [NSMutableArray array];
    unsigned int propertyCount = 0;
    objc_property_t *properties = class_copyPropertyList(klass, &propertyCount);
    
    for (unsigned int i = 0; i < propertyCount; ++i) {
        objc_property_t property = properties[i];
        const char * name = property_getName(property);
        
        [propertyNamesArray addObject:[NSString stringWithUTF8String:name]];
    }
    free(properties);
    
    [propertyListByClass setObject:propertyNamesArray forKey:className];
    NSArray* arr = [JastorRuntimeHelper propertyNames:class_getSuperclass(klass)];
    [propertyNamesArray addObjectsFromArray:arr];
    return propertyNamesArray;
}

+ (Class)propertyClassForPropertyName:(NSString *)propertyName ofClass:(Class)klass {
    if (!propertyClassByClassAndPropertyName) {
        propertyClassByClassAndPropertyName = [[NSMutableDictionary alloc] init];
    }
    
    NSString *key = [NSString stringWithFormat:@"%@:%@", NSStringFromClass(klass), propertyName];
    NSString *value = [propertyClassByClassAndPropertyName objectForKey:key];
    
    if (value) {
        return NSClassFromString(value);
    }
    
    unsigned int propertyCount = 0;
    objc_property_t *properties = class_copyPropertyList(klass, &propertyCount);
    
    const char * cPropertyName = [propertyName UTF8String];
    
    for (unsigned int i = 0; i < propertyCount; ++i) {
        objc_property_t property = properties[i];
        const char * name = property_getName(property);
        if (strcmp(cPropertyName, name) == 0) {
            free(properties);
            NSString *className = [NSString stringWithUTF8String:property_getTypeName(property)];
            [propertyClassByClassAndPropertyName setObject:className forKey:key];
            //we found the property - we need to free
            return NSClassFromString(className);
        }
    }
    free(properties);
    //this will support traversing the inheritance chain
    return [self propertyClassForPropertyName:propertyName ofClass:class_getSuperclass(klass)];
}

@end